#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>//平台设备驱动

#include <linux/of.h> //of函数
#include <linux/of_gpio.h>  
#include <linux/gpio.h>
#include <dt-bindings/gpio/gpio.h>

//字符设备驱动头文件
#include <linux/fs.h> //注册设备号
#include <linux/device.h> //创建设备类和设备文件
#include<linux/slab.h> //kzallo 内存分配
#include <linux/gpio.h> //gpio函数
#include<asm/uaccess.h> //copy_from_user

#include <linux/cdev.h>	 //cdev对象
#include <linux/interrupt.h> //中断
#include <linux/input.h> //KEY_UP定义
#include <linux/sched.h> //wq
#include <linux/string.h> //strcmp

/**
 * 学习目标:  设备树配置 GPIO控制button
 */


#if 0

实现1个按键休眠唤醒读操作

mygpio_button {
		status = "okay";
		compatible = "rk3399,gpio-button";
		btn-irq-gpio = <&gpio1 1 IRQ_TYPE_EDGE_FALLING>;  /* GPIO1_A1 */
		pinctrl-names = "default";
		pinctrl-0 = <&button_ctrl_gpio>;
		
		
		
		
	};
&pinctrl {  //找到&pinctrl这个节点然后添加gpio的pinctrl配置

	mygpio_button {
		button_ctrl_gpio: buton-ctrl-gpio{
		   rockchip,pins =
				   <1 1 RK_FUNC_GPIO &pcfg_pull_up>;
		};
	};
};

#endif 



struct button {
	char *name;
	int code;
	int gpio;
	unsigned int irqno;
	unsigned int flags;
	int have_data;
	wait_queue_head_t wq_head;
};

static int gpio_button; //button引脚号
static int irqno;

//static wait_queue_head_t wq_head;//等待队列头
//static int  have_data;  //表示当前是否有数据可读  可用--1  不可用--0

struct button buttons_set[] = {
	[0] = {
		.name = "key1",
		.code = KEY_UP,
	},
	[1] = {
		.name = "pwr_key",
		.code = KEY_DOWN,
	},

};



//读函数  来读设备的状态
ssize_t gpio_button_show(struct device *dev, struct device_attribute *attr,char *buf)
{

	//判断释放有数据可读,条件不成立休眠
	wait_event_interruptible(buttons_set[0].wq_head, buttons_set[0].have_data);
	buttons_set[0].have_data = 0;	
	//读gpio引脚的电平值
	return sprintf(buf, "%d\n", 123); //可传到用户空间
}

//写函数  操作设备
ssize_t gpio_button_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
{
	
	printk("<xyd> call %s()\n",__FUNCTION__);
	return count;
}




static DEVICE_ATTR(gpio_button,0664,gpio_button_show,gpio_button_store); //名字gpio_led,设备文件权限0664 ,读写函数
//实际上就是定义  struct device_attribute dev_attr_gpio_button



static irqreturn_t button_handler(int irq, void *dev_id)
{
	struct button *p;
	p = (struct button *)dev_id;
	
	//中断修改条件变量
	p->have_data = 1;
	//从等待队列中唤醒阻塞的进程
	wake_up_interruptible(&p->wq_head);
		
	printk("<xyd> call  %s() :%ld\n",__FUNCTION__,jiffies);
	
	return IRQ_HANDLED;
}




// 当DTS节点和驱动的compatible匹配时调用
static int button_driver_probe(struct platform_device *pdev)
{
	//拿到和平台驱动匹配的设备树节点 mybutton_ctrl
	struct device_node *button_ctrl_node = pdev->dev.of_node;
	enum of_gpio_flags flags;
	int ret;

	// 硬件初始化

	
	gpio_button = of_get_named_gpio_flags(button_ctrl_node, "btn-irq-gpio", 0, &flags); //拿到引脚号

	
	irqno = gpio_to_irq(gpio_button); //拿到中断号
	printk("<xyd> irqno=%d\n",irqno);

	buttons_set[0].gpio=gpio_button;
	buttons_set[0].irqno=irqno;
	buttons_set[0].flags=flags;

	ret = request_irq(buttons_set[0].irqno,button_handler,buttons_set[0].flags,buttons_set[0].name,&buttons_set[0]);
	if(ret!=0){
		printk("request_irq fail\n");
		ret=-EINVAL;
		return ret;
	}
	
	printk("<xyd> IRQF_TRIGGER_FALLING = %d\n",IRQF_TRIGGER_FALLING);
	printk("<xyd> flags=%d\n",flags);
	//创建sysfs下的设备节点
	device_create_file(&pdev->dev,&dev_attr_gpio_button);

	// 初始化等待队列头
	init_waitqueue_head(&buttons_set[0].wq_head);
	
	// 条件初始化
	buttons_set[0].have_data = 0; //数据不可用
	
	printk("<xyd> call  %s()\n",__FUNCTION__);
	
	return 0;


}

static int button_driver_remove(struct platform_device *pdev)
{
	free_irq(buttons_set[0].irqno, &buttons_set[0]); //释放中断
	
	device_remove_file(&pdev->dev,&dev_attr_gpio_button);
	
	printk("<xyd> call  %s()\n",__FUNCTION__);
	return 0;
}


static struct of_device_id button_ctrl_match_table [] ={

	{ .compatible = "rk3399,gpio-button", },
	{ },
};




static struct platform_driver  button_drv ={
	.probe = button_driver_probe,
	.remove = button_driver_remove,
	.driver = {
		.name = "mybutton",
		.of_match_table = button_ctrl_match_table,
	},

};


static int __init driver_mod_init(void)
{
	printk("<xyd>==========================\n");
	printk("<xyd> call %s()\n",__FUNCTION__);
	printk("<xyd>==========================\n");
	return platform_driver_register(&button_drv);
}

static void __exit driver_mod_exit(void)
{
	printk("<xyd> call %s()\n",__FUNCTION__);
	platform_driver_unregister(&button_drv);
}

module_init(driver_mod_init);
module_exit(driver_mod_exit);
MODULE_LICENSE("GPL");

